using System;
using System.Collections.Generic;
using System.Text;
using Castle.DynamicProxy;
using System.IO;
using Sio.Mdm.Utils;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting;
using System.Reflection;
using Microsoft.Win32;
using System.Diagnostics;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Timers;
using System.Threading;
using Castle.Core.Interceptor;
using Sio.Mdm.Types;

namespace Sio.Mdm.Server
{
    /// <summary>
    /// Implementation of server (proxy) class.
    /// </summary>
    public partial class Server : MarshalByRefObject, IServer, IInterceptor, IDisposable
    {
        #region Declarations
        /// <summary>
        /// 
        /// </summary>
        public enum WaitType : int
        {
            /// <summary>
            /// WindowsForms application is running
            /// </summary>
            WindowsForms,
            /// <summary>
            /// Conosle application is running
            /// </summary>
            Console,
            /// <summary>
            /// Web application is running
            /// </summary>
            Web,
            /// <summary>
            /// Windows service is running
            /// </summary>
            Service
        }
	    #endregion

        #region Fields
        private static Dictionary<string, TcpServerChannel> Channels = new Dictionary<string, TcpServerChannel>();        
        private static Random Rnd = new Random();
        #endregion

        #region Construction
        public Server()
        {
            Globals.Server = this;
        }
        static Server()
        {
            Resolver.Initialize();
            Logger.Initialize();            
        }
        #endregion
        
        #region Finalizer
        ~Server()
        {   
            Dispose(false);
        }
        #endregion

        #region Properties

        #region Public

        #region Name
        /// <summary>
        /// Get the name of the server
        /// </summary>
        public string Name
        {
            get
            {
                return Config.Name;
            }
        }
        #endregion

        #region Port
        /// <summary>
        /// Get optional port of the server
        /// </summary>
        public int Port
        {
            get
            {
                return Config.Port;
            }
        }
        #endregion 

        #region Interceptors
        private static List<IInvokeInterceptor> _interceptors;
        /// <summary>
        /// Get list of custom interceptors for server
        /// </summary>
        public static List<IInvokeInterceptor> Interceptors
        {
            get
            {
                if (null == _interceptors)
                {
                    _interceptors = new List<IInvokeInterceptor>();
                }

                return _interceptors;
            }
        }
        #endregion

        #endregion

        #region Private

        #region Services
        /// <summary>
        /// Get list of configured services
        /// </summary>
        private List<ServiceInformation> Services
        {
            get
            {
                return Config.Services;
            }
        }
        #endregion

        #region ObjectProxy
        private static ProxyGenerator _objectProxy;
        /// <summary>
        /// One and only instance of proxy generator
        /// </summary>
        private static ProxyGenerator ObjectProxy
        {
            get
            {
                if (null == _objectProxy)
                {
                    _objectProxy = new ProxyGenerator();
                }

                return _objectProxy;
            }
        }
        #endregion

        #region CreatedInstances
        private List<object> _createdServices;
        private List<object> CreatedServices
        {
            get
            {
                if (null == _createdServices)
                {
                    _createdServices = new List<object>();
                }

                return _createdServices;
            }
        }
        #endregion

        #region DummyService
        private static DummyService _dummy;
        private static DummyService DummyService
        {
            get
            {
                if (null == _dummy)
                {
                    _dummy = new DummyService();
                }

                return _dummy;
            }
        }
        #endregion

        #region InternalScheduler
        private static Scheduler _internalScheduler;
        private static Scheduler InternalScheduler
        {
            get
            {
                if (null == _internalScheduler)
                {
                    _internalScheduler = new Scheduler();
                    _internalScheduler.Start();
                }

                return _internalScheduler;
            }
        }
	    #endregion

        #endregion

        #endregion

        #region Methods

        #region Public

        #region Initialize, Destroy, Wait
        /// <summary>
        /// Initialize server on global scale
        /// </summary>
        public static void Initialize()
        {
            Resolver.Initialize();

            EnsureConfigFile();
            ProcessCommandLine();
            CreateAdministrationServer();
        }
        /// <summary>
        /// Initialize server on global scale
        /// </summary>
        /// <param name="args">Argument to pass to command line</param>
        public static void Initialize(params string []args)
        {
            Resolver.Initialize();
            Logger.Initialize();

            EnsureConfigFile();

            // To add command line options to configuration
            Config.ParseCommandLine(args);

            ProcessCommandLine();
            CreateAdministrationServer();
        }
        /// <summary>
        /// Initialize server from specified config file. Optionaly scan path for services and
        /// save configuration to config file.
        /// </summary>
        /// <param name="cacheConfig">Save config for latter use</param>
        /// <param name="pathToScan">Path to recurively scan for modules</param>
        /// <param name="serverConfig">Path to config file to load from</param>
        public static void InitializeFromConfig(string serverConfig, string pathToScan, bool cacheConfig)
        {
            bool hasConfig = false;

            Resolver.Initialize();

            try
            {
                Config.Load(serverConfig);
                hasConfig = true;
            }
            catch 
            {
            }

            try 
            {
                if (!hasConfig)
                {   
                    ServiceScanner.Scan(pathToScan, true);

                    if (cacheConfig)
                    {
                        Config.Save(serverConfig);
                    }
                }

                CreateAdministrationServer();
            }
            catch(Exception ex)
            {
                Logger.WriteFatal("Exception while scanning for services");
                Logger.WriteException(ex);
            }
        }
        /// <summary>
        /// Cleanup resources used by servers on global scale
        /// </summary>
        public static void Destroy()
        {
            // Dispose channel
            DestroyRemoteServer();

            Logger.WriteInformation("Server is shuting down...");

            // Kill logger thread
            Logger.Destroy();
        }
        /// <summary>
        /// Wait for server shutdown based on application type
        /// </summary>
        /// <param name="type">Tpy eo application to use. See <see cref="WaitType"/></param>
        public static void Wait(WaitType type)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region EnsureConfigFile
        private static void EnsureConfigFile()
        {
            if (File.Exists(Config.ConfigPath))
            {
                Logger.WriteInformation("Loading configuration from {0}", Config.ConfigPath);
                Config.Load();
            }
            else
            {
                Logger.WriteWarning("Config not found. Autoconfig in pogress.");

                ServiceScanner.Scan(null, true);
                Config.Save(Config.ConfigPath);

                Logger.WriteInformation("Config written to {0}", Config.ConfigPath);
            }
        }
        #endregion

        #region ProcessCommandLine
        private static void ProcessCommandLine()
        {
            try
            {
                // Running in client mode
                if (Config.CommandLine.HasParameter("client"))
                {
                    return;
                }

                if ( Config.CommandLine.HasParameter("input") )
                {
                    Config.Load(Config.CommandLine["input"]);                    
                }

                if (Config.CommandLine.HasParameter("name"))
                {
                    // Pragmatic way for remoting in case we need it            
                    Config._name = Config.CommandLine["name"];
                }

                if (Config.CommandLine.HasParameter("port"))
                {
                    // Pragmatic way for remoting in case we need it            
                    Config._port = Config.CommandLine.GetParameterAsInteger("port");
                }

                if (Config.CommandLine.HasParameter("scan"))
                {
                    // Remove current services that where parsed
                    Config.Services.Clear();

                    if (Config.CommandLine.HasParameter("security"))
                    {
                        ServiceScanner.ServiceFound += ResolveSecurityObjects;
                    }
                    
                    // Scan for services
                    ServiceScanner.Scan(Config.CommandLine["scan"], true);
                }

                if (Config.CommandLine.HasParameter("output"))
                {
                    Config.Save(Config.CommandLine["output"]);
                    Logger.WriteInformation("Configuration saved!");
                    return;
                }

                if (Config.CommandLine.HasParameter("remote") || Config.IsRemoting)
                {
                    // Pragmatic way for remoting in case we need it            
                    CreateRemoteServer();
                }

                if (Config.CommandLine.HasParameter("install"))
                {                    
                    InstallService();                    
                }

                if (Config.CommandLine.HasParameter("service"))
                {
                    //Service service = new Service();
                    
                    //service.ServiceName = String.IsNullOrEmpty(Config.CommandLine["service"]) ?
                    //    Config.Name : Config.CommandLine["service"];                    

                    //System.ServiceProcess.ServiceBase.Run(service);
                }

                if (Config.CommandLine.HasParameter("help") || Config.CommandLine.HasParameter("?") )
                {
                    ShowHelp();
                }
            }
            catch (Exception ex)
            {
                Logger.WriteException(ex);
            }
        }
        #endregion

        #region InstallService
        private static void InstallService()
        {
            Logger.WriteInformation("Installing service {0}", Config.Name);

            try
            {
                string installUtil = GetFrameworkInstallUtilPath();

                if (String.IsNullOrEmpty(installUtil))
                {
                    Logger.WriteError(".NET framework is missing. At least .NET framework version 2 is required!");
                    return;
                }

                ProcessStartInfo startInfo = new ProcessStartInfo(installUtil);
                Process process = new Process();
                string currentProcess = AppDomain.CurrentDomain.FriendlyName;

                startInfo.Arguments = Path.GetFullPath(currentProcess);
                startInfo.CreateNoWindow = true;

                process.StartInfo = startInfo;

                process.Start();
                process.WaitForExit();

                Logger.WriteInformation("Service installed.");
            }
            catch (Exception ex)
            {
                Logger.WriteError("Service install failed.");
                Logger.WriteDebug(ex.ToString());
            }
        }
        #endregion

        #region GetService
        /// <summary>
        /// Get service based on it's implemented interface.
        /// </summary>
        /// <param name="interfaceType">Interface type to search services for</param>
        /// <returns>Instance of service that implements passed interface or null if service not found</returns>
        public IService GetService(Type interfaceType)
        {
            if ( !interfaceType.IsInterface )
            {
                throw new ArgumentException("Service can be retreived by implementing interface only!");
            }

            Logger.WriteDebug("Service by interface requested ({0})", interfaceType.Name);

            foreach (ServiceInformation service in Services)
            {
                if (service.Implements(interfaceType))
                {
                    return CreateServiceInstance(service);
                }
            }

            ServiceInformation info = OnServiceNotFound(interfaceType);

            if (info == null)
            {
                Logger.WriteDebug("Service not found ({0})", interfaceType.Name);
            }
            
            return info == null? null: CreateServiceInstance(info);
        }
        /// <summary>
        /// Get typed service
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IService GetService<T>() where T: class, IService
        {
            return GetService(typeof(T)) as T;
        }
        /// <summary>
        /// Get service based on it's name.
        /// </summary>
        /// <param name="serviceName">Name of the service to retreive.</param>
        /// <returns>Instance of service that implements passed interface or null if service not found</returns>
        public IService GetService(string serviceName)
        {
            Logger.WriteDebug("Service by name requested ({0})", serviceName);

            foreach (ServiceInformation service in Services)
            {
                if (String.Compare(service.Name, serviceName) == 0)
                {
                    return CreateServiceInstance(service);
                }
            }

            ServiceInformation info = OnServiceNotFound(serviceName);

            if (info == null)
            {
                Logger.WriteDebug("Service not found ({0})", serviceName);
            }

            return info == null ? null : CreateServiceInstance(info);
        }
        #endregion

        #region HasService
        /// <summary>
        /// Test wheater service implementing specified interface is configured for this server
        /// </summary>
        /// <param name="interfaceType">Interface type to test for</param>
        /// <returns>True if service is available</returns>
        public bool HasService(Type interfaceType)
        {
            foreach (ServiceInformation service in Services)
            {
                if (service.Implements(interfaceType))
                {
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Test wheater service with specified name is configured for this server
        /// </summary>
        /// <param name="serviceName">Name of service to test for</param>
        /// <returns>True if service is configured</returns>
        public bool HasService(string serviceName)
        {
            foreach (ServiceInformation service in Services)
            {
                if (String.Compare(service.Name, serviceName) == 0)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion

        #region GetServiceInfo
        /// <summary>
        /// Get information on service implementation
        /// </summary>
        /// <param name="serviceName">Name of service to get inforamtion for</param>
        /// <returns><see cref="ServiceInfo"/> instance or null if no service exist</returns>
        public ServiceInfo GetServiceInfo(string serviceName)
        {
            ServiceInformation info = Services.Find(new Predicate<ServiceInformation>(
                delegate(ServiceInformation item)
                {
                    return String.Compare(item.Name, serviceName) == 0;
                }
            ));

            if (info == null)
            {
                return null;
            }

            ServiceInfo result = new ServiceInfo();

            result.Name = info.Name;
            result.ImplementingType = info.ImplementationType;
            result.DefaultInterface = info.DefaultInterface;

            return result;
        }
        #endregion
        
        #endregion

        #region Protected

        #region Dispose
        /// <summary>
        /// Dispose resources used by this instance
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            // We don't care wheater we're disposing or not
            // it's important tha we clean up cache
            CollectServices();
        }
        #endregion

        #region OnServiceNotFound
        /// <summary>
        /// Invoked when service could not be found in config. When overriding
        /// in base class you should return proper <see cref="ServiceInformation"/>
        /// class.
        /// </summary>        
        /// <param name="serviceName">service name</param>
        protected virtual ServiceInformation OnServiceNotFound(string serviceName)
        {
            return null;
        }
        /// <summary>
        /// Invoked when service could not be found in config. When overriding
        /// in base class you should return proper <see cref="ServiceInformation"/>
        /// class.
        /// </summary>        
        /// <param name="interfaceType">Type of interface being requested</param>
        protected virtual ServiceInformation OnServiceNotFound(Type interfaceType)
        {
            return null;
        }
        #endregion

        #endregion

        #region Private

        #region CollectServices
        private void CollectServices()
        {
            if (_createdServices != null)
            {
                foreach (object service in CreatedServices)
                {
                    if (service is IDisposable)
                    {
                        (service as IDisposable).Dispose();
                    }
                }

                CreatedServices.Clear();
                _createdServices = null;
            }
        }
        #endregion

        #region CreateServiceInstance
        private Sio.Mdm.Server.IService CreateServiceInstance(ServiceInformation service)
        {   
            Type type = Type.GetType(service.DefaultInterface, false, true);            
            Sio.Mdm.Server.IService serviceInstance = null;

            Logger.WriteInformation("Creating service instance {0}", type.ToString());

            switch (service.Type)
            {
                case ServiceInformation.ServiceType.Disk:
                    try
                    {
                        Logger.WriteDebug("Loading type from {0}", service.Address);
                        System.Runtime.Remoting.ObjectHandle handle = Activator.CreateInstanceFrom(service.Address, service.ShortImplementationType);
                        serviceInstance = handle.Unwrap() as IService;
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteError("Service creation failed [disk].");
                        Logger.WriteException(ex);
                    }
                    break;
                case ServiceInformation.ServiceType.Remoting:
                    try
                    {
                        serviceInstance = Activator.GetObject(type, service.Address) as IService;
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteError("Service creation failed [remoting].");
                        Logger.WriteException(ex);
                    }
                    break;
                case ServiceInformation.ServiceType.WebService:
                    try
                    {
                        WebServiceProxy proxy = new WebServiceProxy(service);
                        serviceInstance = ObjectProxy.CreateInterfaceProxyWithTarget(type, proxy, proxy) as IService;// .CreateProxy(type, proxy, proxy) as IService;
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteError("Service creation failed [web service].");
                        Logger.WriteException(ex);
                    }
                    break;
            }
            
            // For latter disposing. Have a look at Dispose function
            if (serviceInstance != null)
            {
                CreatedServices.Add(serviceInstance);
                AssignServerInstance(serviceInstance);

                // Restart collecting of services     
                // TODO: Rewrite collecting
            }
            else
            {
                return ObjectProxy.CreateInterfaceProxyWithTarget(type, DummyService, DummyService) as IService;
            }

            return ObjectProxy.CreateInterfaceProxyWithTarget(type, serviceInstance, this) as IService;
        }
        #endregion

        #region AssignServerInstance
        private void AssignServerInstance(object serviceInstance)
        {
            if (serviceInstance is IRequireServerInstance)
            {
                (serviceInstance as IRequireServerInstance).Server = this;
            }

            // Replaced with IRequireServerInstance
            // Check wheater this is internal service and assign instance of server
            //if (serviceInstance is BaseService)
            //{
            //    FieldInfo field = typeof(BaseService).GetField("_server", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            //    if ( field != null )
            //    {
            //        // There can always be field with same name but not
            //        // of proper type. Eat up all excpetions here.
            //        try { field.SetValue(serviceInstance, this); } catch { }
            //    }
            //}
        }
        #endregion

        #region FindSessionArgument
        private Sio.Mdm.Types.Session FindSessionArgument(params object[] args)
        {
            for (int i = args.Length - 1; i > -1; i--)
            {
                if (args[i] is Sio.Mdm.Types.Session)
                {
                    return args[i] as Sio.Mdm.Types.Session;
                }
            }

            return null;
        }
        #endregion

        #region ShowHelp
        private static void ShowHelp()
        {
            Console.WriteLine("SIO.MDM.Server version 1.0.");
            Console.WriteLine("");
            Console.WriteLine("Usage:");
            Console.WriteLine("input:<config file> - read config from specified file");
            Console.WriteLine("name:<server name> - specify name for server (or service)");
            Console.WriteLine("port:<port number> - specify port to use for remoting");
            Console.WriteLine("remote - run server as remoting server");
            Console.WriteLine("scan:<path to scan> - scan path for services. Use with 'output' paramter");
            Console.WriteLine("security:<file_path> - create security objects for scanned services and output result to file passed.");
            Console.WriteLine("output:<config file> - write server configuration to config file");
            Console.WriteLine("service - run server as service. Make sure service is installed and proper name is set in config or command line.");
        }  
        #endregion

        #warning To ne bo kompatibilno z linuxom!
        //to-do Potrebno odstraniti vse kar se sklicuje na win32
        #region GetFrameworkInstallUtilPath
        private static string GetFrameworkInstallUtilPath()
        {
            try
            {
                RegistryKey software = Registry.LocalMachine.OpenSubKey("software\\Microsoft\\.NetFramework");
                string value = software.GetValue("InstallRoot") as string;
                string []folders = Directory.GetDirectories(value, "v?.*", SearchOption.TopDirectoryOnly);
                List<string> versions = new List<string>(folders);

                // Find top version
                versions.Sort(new Comparison<string>
                (
                    delegate(string val1, string val2)
                    {
                        return String.Compare(val2, val1);
                    }
                ));

                // Find where InstallUtil.exe resides
                value = versions.Find(new Predicate<string>
                (
                    delegate(string path)
                    {
                        return File.Exists(String.Format("{0}\\InstallUtil.exe", path));
                    }
                ));

                return Path.Combine(value, "InstallUtil.exe");
            }
            catch (Exception ex)
            {
                ex.ToString();
            }

            return null;
        }
        #endregion

        #region ResolveSecurityObjects
        private static void ResolveSecurityObjects(ServiceInformation service)
        {
            try
            {
                Type type = Type.GetType(service.DefaultInterface);
                string fileName = Config.CommandLine["security"];
                MemberInfo[] members = type.GetMembers();

                foreach (MemberInfo member in members)
                {
                    if (String.IsNullOrEmpty(fileName))
                    {
                        Console.WriteLine(member.ToString());
                    }

                }
            }
            catch (Exception ex)
            {
                Logger.WriteError("Error resolving security objects for passed service {0}", service.Name);
                Logger.WriteException(ex);
            }
        } 
        #endregion
        
        #endregion

        #region Internal

        #region CreateRemoteServer
        internal static void CreateRemoteServer(string name, int port)
        {
            BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
            provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
            CreateRemoteServer(name, port, provider);
        }
        internal static void CreateRemoteServer(string name, int port, IServerChannelSinkProvider provider)
        {
            if (Channels.ContainsKey(String.Format("{0}:{1}", name, port)))
            {
                return;
            }
            
            TcpServerChannel channel = new TcpServerChannel(name, port, provider);

            ChannelServices.RegisterChannel(channel, false);

            RemotingConfiguration.RegisterWellKnownServiceType
            (
                typeof(Server),
                Sio.Mdm.Server.Config.Name,
                WellKnownObjectMode.Singleton
            );

            Channels.Add(String.Format("{0}:{1}", name, port), channel);
        }
        internal static void CreateRemoteServer()
        {
            try
            {
                // TODO: Change logging ...
                Logger.WriteInformation("Starting server...");

                CreateRemoteServer(Sio.Mdm.Server.Config.Name, Sio.Mdm.Server.Config.Port);

                Logger.WriteInformation("Server started {0}:{1}", Sio.Mdm.Server.Config.Name, Sio.Mdm.Server.Config.Port);
                Logger.WriteInformation("Available services:");

                foreach (ServiceInformation service in Config.Services)
                {
                    Logger.WriteInformation("{0}", service.Name);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError("Server start failed");
                Logger.WriteException(ex);
            }
        }
        #endregion

        #region DestroyRemoteServer
        internal static void DestroyRemoteServer()
        {
            foreach (KeyValuePair<string, TcpServerChannel> entry in Channels)
            {
                try
                {
                    ChannelServices.UnregisterChannel(entry.Value);
                }
                catch (ThreadAbortException)
                {
                }
                catch (ObjectDisposedException)
                {
                    // Ignore disposed excpetion                    
                }
                catch (Exception ex)
                {
                    Logger.WriteError("Failed to close channel");
                    Logger.WriteException(ex);
                }
            }

            Channels.Clear();
        }
        #endregion  

        #endregion

        #endregion

        #region IInterceptor Members
        [DebuggerStepThrough()]
        /// <summary>
        /// This is the heart of server security and loggin. Implemented of course
        /// explicitly since we don't want for anybody to call it directly.
        /// </summary>
        /// <param name="invocation">Invocation inforamtion</param>
        /// <param name="args">Arguments passed to function</param>
        /// <returns>One of the <see cref="Error"/> entity.</returns>
        private object Intercept(IInvocation invocation, params object []args)
        {
            Logging.WriteEnterCall(invocation, args);

            // Let us find session
            Sio.Mdm.Types.Session session = FindSessionArgument(args);

            // Check wheater security permits call
            // TODO: How about anonymous sessions
            if (!SecurityManager.CheckSecurity(session, invocation, this, args))
            {
                Logging.WriteSecurityLog(invocation);
                return DummyService.GetErrorReturnValue(invocation);
            }

            InvocationError error = null;
            object retVal = null;

            // Now call service and make sure exceptions are handled
            try
            {
                foreach (IInvokeInterceptor interceptor in Interceptors)
                {
                    try
                    {
                        if (interceptor is IRequireServerInstance)
                        {
                            (interceptor as IRequireServerInstance).Server = this;
                        }

                        if (!interceptor.BeforeInvoke(invocation.Method, args, invocation.Proxy.ToString(), out retVal))
                        {
                            return retVal;
                        }
                    }
                    catch { }
                }

                invocation.Proceed();
                Logging.WriteLeaveCall(invocation, retVal, args);
                
                retVal = invocation.ReturnValue;
            }
            catch (Exception ex)
            {
                Logger.WriteError("Service implementation invocation error");
                Logger.WriteException(ex);
                error = new InvocationError(ex);
            }

            foreach (IInvokeInterceptor interceptor in Interceptors)
            {
                try
                {
                    interceptor.AfterInvoke(invocation.Method, args, retVal, error);
                }
                catch { }
            }

            return error == null ?
                retVal : DummyService.GetErrorReturnValue(invocation, error.Exception as Exception);
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {            
            Dispose(true);
        }
        #endregion

        #region IInterceptor Members
        [DebuggerStepThrough()]
        void IInterceptor.Intercept(IInvocation invocation)
        {
            if (invocation.Method.ReturnType.IsSubclassOf(typeof(Result)) ||
                invocation.Method.ReturnType == typeof(Result)
               )
            {
                invocation.ReturnValue = Intercept(invocation, invocation.Arguments);
            }
            else
            {
                throw new Exception("Service functions must have return type of 'Sio.Mdm.Types.Result'");
            }            
        }

        #endregion
    }
}
